/*
 * Created by SharpDevelop.
 * User: michaelo
 * Date: 18/08/2004
 * Time: 15:51
 * 
 * To change this template use Tools | Options | Coding | Edit Standard Headers.
 */

using System;
using System.Text.RegularExpressions;
using System.Windows.Forms;
using System.Collections;
using System.Text;
using Orz.Gui.Forms;

namespace Orz.LogManager 
{
	public enum FilterMode
	{
		KeepOnlyMatchingLines,
		RemoveMatchingLines
	}
	
	/// <summary>
	/// Description of Filterer.	
	/// </summary>
	public class Filterer : Finder
	{
		protected FilterMode mode_i = FilterMode.RemoveMatchingLines;
		
		
		private new bool Find (RichTextBox rtb_p)
		{
			// Not implemented
			return false;
		}
		
		
		
		public void Filter (RichTextBox rtb_p)
		{
			AddToPreviousPatterns();
		    
		    IList matchingLineNos = null;
		    
			if (WildcardOptions.None == Wildcards)
			{
				matchingLineNos = SimpleFindLines (rtb_p);
			}
			else
			{
				matchingLineNos = RegExpFindLines (rtb_p);
			}					
			
			if (FilterMode.KeepOnlyMatchingLines == Mode)
			{
				matchingLineNos = NegateLineNosList(rtb_p.Lines.Length, matchingLineNos);				
			}
			
			rtb_p.Hide();
			StripLines (rtb_p, matchingLineNos);
			rtb_p.Show();
		}
		
		
		
		
		public FilterMode Mode
		{
			get { return mode_i;  }
			set { mode_i = value; }
		}
		
		
		
		
		public override string ToString()
		{
		    StringBuilder sb = new StringBuilder();
			
			sb.Append (base.ToString());
			sb.Append (", ");
			sb.Append ("Mode=<" + Mode + ">");
		    
		    return sb.ToString();
		}
		
		
		
		
		
		
		
		
		
		///
		/// <summary>
		/// Searches the entire textbox text for lines that match the filter criteria.
		/// </summary>
		/// <param name="rtb_p">The textbox to be filtered.</param>
		/// <returns>A list of the line numbers that match the filter criterial.</returns>
		///		
		private IList SimpleFindLines (RichTextBox rtb_p)
		{
			string[] lines = rtb_p.Lines;
			
			string sText = rtb_p.Text;
			
			int iLineStartPos = 0;
			IList matchingLineNos = new ArrayList();
			
			for (int iLine = 0; iLine < lines.Length; iLine++)
			{
				int iLineLength = lines[iLine].Length;
				string sLine = sText.Substring (iLineStartPos, iLineLength);
				
				if (LineMatches (sLine))
				{
					matchingLineNos.Add (iLine);
				}
				
				iLineStartPos += iLineLength + 1;				
			}
			
			return matchingLineNos;
		}
		
		
		
		
		
		private IList RegExpFindLines (RichTextBox rtb_p)
		{
			Regex re = CreateRegularExpression();
			MatchCollection matches = re.Matches (rtb_p.Text);
			
			IList matchingLineNos = new ArrayList();
			int iLastLineStored = -1;
			
			foreach (Match match in matches)
			{
				int iLine = rtb_p.GetLineFromCharIndex(match.Index);
				
				if (iLine < iLastLineStored)
				{
					throw new System.ArgumentException ("Line " + iLine + " is less than " + iLastLineStored);
				}
				else if (iLine > iLastLineStored)
				{
					matchingLineNos.Add (iLine);
				}
			}
			
			return matchingLineNos;
		}
		
		
		
		private bool LineMatches (string sLine_p)
		{
			string sPattern = Pattern;
			
			if (MatchCase)
			{
				sPattern = sPattern.ToUpper();
				sLine_p = sLine_p.ToUpper();
			}
			
			int iStartIndex = sLine_p.IndexOf(sPattern, 0);
			
			while (-1 != iStartIndex)
			{
				if (! WholeWordsOnly) return true;
				
				bool isWholeWord = true;
				
				if (0 != iStartIndex  &&  !IsNonWordChar (sLine_p[iStartIndex-1]))
				{
					isWholeWord = false;
				}
				else if (iStartIndex+sPattern.Length < sLine_p.Length  &&  !IsNonWordChar (sLine_p[iStartIndex+sPattern.Length+1]))
				{
					isWholeWord = false;
				}
					
				if (isWholeWord) return true;
				
				iStartIndex = sLine_p.IndexOf(sPattern, iStartIndex + sPattern.Length);	
			}
						
			return false;
		}
		
		
		
		private bool IsNonWordChar (char c_p)
		{
			string sWordChars = "_ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";			
			string sChar = c_p.ToString().ToUpper();
			
			return (-1 == sWordChars.IndexOf (sChar));			
		}
		
		
		
		
		private void StripLines (RichTextBox rtb_p, IList lineNosToStrip)
		{
			if (rtb_p is SpecialRichTextBox)
			{
				SpecialRichTextBox srtb = (SpecialRichTextBox) rtb_p;
				srtb.AllowPaint = false;
				srtb.RemoveLines (lineNosToStrip);
				srtb.AllowPaint = true;
			}
			else
			{
				throw new InvalidOperationException ("Not supported on standard RichTextBox");
			}
		}
		
		
		
		private IList NegateLineNosList (int iMaxLength_p, IList original_p)
		{
			IList reversedList = new ArrayList();
			for (int iLine = 0; iLine < iMaxLength_p; iLine++)
			{
				reversedList.Add (iLine);
			}
			
			for (int iIndex = original_p.Count-1; iIndex >= 0; iIndex--)
			{
				int iLine = (int) original_p[iIndex];
				reversedList.RemoveAt(iLine);
			}
			
			return reversedList;
		}
	}
}
